package handler

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"myai/core"
	"myai/core/types"
	"myai/service"
	"myai/service/oss"
	"myai/service/suno"
	"myai/store/model"
	"myai/store/model/req"
	"myai/store/vo"
	"myai/utils"
	"myai/utils/resp"
	"time"
)

type SunoHandler struct {
	BaseHandler
	sunoService *suno.Service
	uploader    *oss.UploaderManager
	userService *service.UserService
}

func NewSunoHandler(app *core.AppServer, db *gorm.DB, service *suno.Service,
	uploader *oss.UploaderManager, userService *service.UserService) *SunoHandler {
	return &SunoHandler{
		BaseHandler: BaseHandler{
			App: app,
			DB:  db,
		},
		sunoService: service,
		uploader:    uploader,
		userService: userService,
	}
}

// Create godoc
// @Summary      创建一个生成音乐任务
// @Description  创建一个生成音乐任务
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        req   body     req.SunoJobCreateReq  true  "生成音乐参数"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/suno/create [post]
// @Security ApiKeyAuth
func (h *SunoHandler) Create(c *gin.Context) {
	var data req.SunoJobCreateReq
	if err := c.ShouldBindJSON(&data); err != nil {
		resp.ERROR(c, types.InvalidArgs)
		return
	}

	user, err := h.GetLoginUser(c)
	if err != nil {
		resp.NotAuth(c)
		return
	}

	if user.Power < h.App.SysConfig.SunoPower {
		resp.ERROR(c, "您的算力不足，请充值后重试！")
		return
	}

	// 歌曲拼接
	if data.SongId != "" && data.Type == 3 {
		var song model.SunoJob
		if err := h.DB.Where("song_id = ?", data.SongId).First(&song).Error; err == nil {
			data.Instrumental = song.Instrumental
			data.Model = song.ModelName
			data.Tags = song.Tags
		}
		// 拼接歌词
		var refSong model.SunoJob
		if err := h.DB.Where("song_id = ?", data.RefSongId).First(&refSong).Error; err == nil {
			data.Prompt = fmt.Sprintf("%s\n%s", song.Prompt, refSong.Prompt)
		}
	}

	if data.Lyrics != "" {
		data.Prompt = data.Lyrics
	}

	task := types.SunoTask{
		ClientId:     data.ClientId,
		UserId:       int(user.Id),
		Type:         data.Type,
		Title:        data.Title,
		RefTaskId:    data.RefTaskId,
		RefSongId:    data.RefSongId,
		ExtendSecs:   data.ExtendSecs,
		Prompt:       data.Prompt,
		Tags:         data.Tags,
		Model:        data.Model,
		Instrumental: data.Instrumental,
		SongId:       data.SongId,
		AudioURL:     data.AudioURL,
	}

	// 插入数据库
	job := model.SunoJob{
		UserId:       task.UserId,
		Prompt:       data.Prompt,
		Instrumental: data.Instrumental,
		ModelName:    data.Model,
		TaskInfo:     utils.JsonEncode(task),
		Tags:         data.Tags,
		Title:        data.Title,
		Type:         data.Type,
		RefSongId:    data.RefSongId,
		RefTaskId:    data.RefTaskId,
		ExtendSecs:   data.ExtendSecs,
		Power:        h.App.SysConfig.SunoPower,
		SongId:       utils.RandString(32),
	}

	tx := h.DB.Create(&job)
	if tx.Error != nil {
		resp.ERROR(c, "插入Suno任务失败："+tx.Error.Error())
		return
	}

	// 创建任务
	task.Id = job.Id
	h.sunoService.PushTask(task)

	// 扣减算力
	err = h.userService.DecreasePower(int(job.UserId), job.Power, model.PowerLog{
		Type:      types.PowerConsume,
		Model:     job.ModelName,
		Remark:    fmt.Sprintf("Suno 文生歌曲：%s", job.ModelName),
		CreatedAt: time.Now(),
	})
	if err != nil {
		resp.ERROR(c, "扣减算力失败："+err.Error())
		return
	}
	resp.SUCCESS(c)
}

// List godoc
// @Summary      获取音乐任务列表
// @Description  获取音乐任务列表
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        page        query     int  true  "页码"
// @Param        page_size   query     int  true  "每页数量"
// @Success      200  {object}  types.BizVo{data=vo.Page}
// @Router       /api/suno/list [get]
// @Security ApiKeyAuth
func (h *SunoHandler) List(c *gin.Context) {
	userId := h.GetLoginUserId(c)
	page := h.GetInt(c, "page", 1)
	pageSize := h.GetInt(c, "page_size", 20)
	session := h.DB.Session(&gorm.Session{}).Where("user_id", userId)

	// 统计总数
	var total int64
	session.Model(&model.SunoJob{}).Count(&total)
	if page > 0 && pageSize > 0 {
		offset := (page - 1) * pageSize
		session = session.Offset(offset).Limit(pageSize)
	}

	var list []model.SunoJob
	err := session.Order("id desc").Find(&list).Error
	if err != nil {
		resp.ERROR(c, "查询任务失败："+err.Error())
		return
	}

	// 初始化续写关系
	songIds := make([]string, 0)
	for _, v := range list {
		if v.RefTaskId != "" {
			songIds = append(songIds, v.RefSongId)
		}
	}

	var tasks []model.SunoJob
	h.DB.Where("song_id IN ?", songIds).Find(&tasks)
	songMap := make(map[string]model.SunoJob)
	for _, t := range tasks {
		songMap[t.SongId] = t
	}

	// 转换为 vo
	items := make([]vo.SunoJob, 0)
	for _, v := range list {
		var item vo.SunoJob
		err = utils.CopyObject(v, &item)
		if err != nil {
			log.Errorf("拷贝数据出现错误：%v", err)
			continue
		}
		item.CreatedAt = v.CreatedAt.Unix()
		if s, ok := songMap[v.RefSongId]; ok {
			item.RefSong = map[string]interface{}{
				"id":    s.Id,
				"title": s.Title,
				"cover": s.CoverURL,
				"audio": s.AudioURL,
			}
		}
		items = append(items, item)
	}
	resp.SUCCESS(c, vo.NewPage(total, page, pageSize, items))
}

// Remove godoc
// @Summary      删除音乐任务
// @Description  删除音乐任务
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        id        query     int  true  "音乐任务Id"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/suno/remove [get]
// @Security ApiKeyAuth
func (h *SunoHandler) Remove(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	var job model.SunoJob
	// 查询任务
	err := h.DB.Where("id = ?", id).Where("user_id", userId).First(&job).Error
	if err != nil {
		resp.ERROR(c, "查询任务时出现错误："+err.Error())
		return
	}
	// 只有失败或者超时的任务才能删除
	/*if job.Progress != service.FailTaskProgress || time.Now().Before(job.CreatedAt.Add(time.Minute*10)) {
		resp.ERROR(c, "只有失败和超时(10分钟)的任务才能删除！")
		return
	}*/

	// 只有失败或者已完成的任务可以删除
	if !(job.Progress == service.FailTaskProgress || job.Progress == 100) {
		resp.ERROR(c, "只有失败或者已完成的任务可以删除！")
		return
	}

	// 删除任务
	err = h.DB.Delete(&job).Error
	if err != nil {
		resp.ERROR(c, "删除任务出现错误："+err.Error())
		return
	}
	// 删除文件
	_ = h.uploader.GetUploadHandler().Delete(job.CoverURL)
	_ = h.uploader.GetUploadHandler().Delete(job.AudioURL)
}

// Publish godoc
// @Summary      发布、或者取消发布
// @Description  发布、或者取消发布
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        id          query     int  true  "音乐任务Id"
// @Param        action   	 query     bool true  "任务是否发布"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/suno/publish [get]
// @Security ApiKeyAuth
func (h *SunoHandler) Publish(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	publish := h.GetBool(c, "publish")
	err := h.DB.Model(&model.SunoJob{}).Where("id", id).Where("user_id", userId).UpdateColumn("publish", publish).Error
	if err != nil {
		resp.ERROR(c, "操作失败："+err.Error())
		return
	}
	resp.SUCCESS(c)
}

// Update godoc
// @Summary      修改标题和封面
// @Description  修改标题和封面
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        req   body     req.SunoJobUpdateReq  true  "修改音乐标题和封面参数"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/suno/update [post]
// @Security ApiKeyAuth
func (h *SunoHandler) Update(c *gin.Context) {
	var data req.SunoJobUpdateReq
	if err := c.ShouldBindJSON(&data); err != nil {
		resp.ERROR(c, types.InvalidArgs)
		return
	}

	if data.Id == 0 || data.Title == "" || data.Cover == "" {
		resp.ERROR(c, types.InvalidArgs)
		return
	}

	userId := h.GetLoginUserId(c)
	var item model.SunoJob
	if err := h.DB.Where("id", data.Id).Where("user_id", userId).First(&item).Error; err != nil {
		resp.ERROR(c, "查询suno任务出现错误："+err.Error())
		return
	}

	item.Title = data.Title
	item.CoverURL = data.Cover

	if err := h.DB.Updates(&item).Error; err != nil {
		resp.ERROR(c, "更新失败："+err.Error())
		return
	}
	resp.SUCCESS(c)
}

// Detail godoc
// @Summary      歌曲详情
// @Description  歌曲详情
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        song_id        query     string  true  "续写的歌曲ID"
// @Success      200  {object}  types.BizVo{data=vo.SunoJob}
// @Router       /api/suno/detail [get]
// @Security ApiKeyAuth
func (h *SunoHandler) Detail(c *gin.Context) {
	songId := c.Query("song_id")
	if songId == "" {
		resp.ERROR(c, types.InvalidArgs)
		return
	}
	var item model.SunoJob
	if err := h.DB.Where("song_id", songId).First(&item).Error; err != nil {
		resp.ERROR(c, "查询歌曲信息失败："+err.Error())
		return
	}
	// 读取用户信息
	var user model.User
	if err := h.DB.Where("id", item.UserId).First(&user).Error; err != nil {
		resp.ERROR(c, "查询用户信息失败："+err.Error())
		return
	}

	var itemVo vo.SunoJob
	if err := utils.CopyObject(item, &itemVo); err != nil {
		resp.ERROR(c, "拷贝数据对象出现错误："+err.Error())
		return
	}

	itemVo.CreatedAt = item.CreatedAt.Unix()
	itemVo.User = map[string]interface{}{
		"nickname": user.Nickname,
		"avatar":   user.Avatar,
	}
	resp.SUCCESS(c, itemVo)
}

// Play godoc
// @Summary      增加歌曲播放次数
// @Description  增加歌曲播放次数
// @Tags         前台-SUNO音乐
// @Accept       json
// @Produce      json
// @Param        song_id        query     string  true  "续写的歌曲Id"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/suno/play [get]
// @Security ApiKeyAuth
func (h *SunoHandler) Play(c *gin.Context) {
	songId := c.Query("song_id")
	if songId == "" {
		resp.ERROR(c, types.InvalidArgs)
		return
	}
	h.DB.Model(&model.SunoJob{}).Where("song_id", songId).UpdateColumn("play_times", gorm.Expr("play_times + ?", 1))
}
